<template>
  <div id="app">
    <div class="container">
      <div class="header">
        <h1>Text2SQL评估系统</h1>
        <p>基于LLM的SQL生成准确率评估工具</p>
      </div>
      
      <!-- 单个SQL评估 -->
      <div class="card">
        <div class="card-header">
          <el-icon><Edit /></el-icon> 单个SQL评估
        </div>
        <div class="card-body">
          <div class="form-section">
            <div class="form-group">
              <label>问题描述</label>
              <el-input
                v-model="singleEvaluation.question"
                type="textarea"
                :rows="3"
                placeholder="请输入自然语言问题..."
              />
            </div>
            
            <div class="form-row">
              <div class="form-group">
                <label>预测的SQL语句</label>
                <el-input
                  v-model="singleEvaluation.predictedSql"
                  type="textarea"
                  :rows="3"
                  placeholder="请输入预测的SQL语句..."
                />
              </div>
              <div class="form-group">
                <label>标准SQL语句</label>
                <el-input
                  v-model="singleEvaluation.groundTruthSql"
                  type="textarea"
                  :rows="3"
                  placeholder="请输入标准SQL语句..."
                />
              </div>
            </div>
            
            <el-button 
              type="primary" 
              @click="evaluateSingle" 
              :loading="isLoading"
              size="large"
            >
              {{ isLoading ? '评估中...' : '开始评估' }}
            </el-button>
          </div>
          
          <div v-if="singleResult" class="result-section">
            <el-card class="result-card">
              <template #header>
                <div class="result-title">SQL准确率评估结果</div>
              </template>
              <div class="result-content">
                <p><strong>完全匹配:</strong> 
                  <el-tag :type="singleResult.sql_accuracy.exact_match ? 'success' : 'danger'">
                    {{ singleResult.sql_accuracy.exact_match ? '是' : '否' }}
                  </el-tag>
                </p>
                <p><strong>语义相似度:</strong> 
                  <el-tag :type="getScoreType(singleResult.sql_accuracy.semantic_score)">
                    {{ (singleResult.sql_accuracy.semantic_score * 100).toFixed(1) }}%
                  </el-tag>
                </p>
                <p><strong>标准化预测SQL:</strong> <code>{{ singleResult.sql_accuracy.normalized_predicted }}</code></p>
                <p><strong>标准化标准SQL:</strong> <code>{{ singleResult.sql_accuracy.normalized_ground_truth }}</code></p>
              </div>
            </el-card>
            
            <el-card v-if="singleResult.ragas_evaluation || singleResult.ragasEvaluation" class="result-card">
              <template #header>
                <div class="result-title">RAGAS评估结果</div>
              </template>
              <div class="result-content">
                <p><strong>忠实度:</strong> 
                  <el-tag :type="getScoreType(singleFaithfulnessMean)">
                    {{ (singleFaithfulnessMean * 100).toFixed(1) }}%
                  </el-tag>
                </p>
                <p><strong>答案相关性 (开源模型):</strong> 
                  <el-tag :type="getScoreType(singleAnswerRelMean)">
                    {{ (singleAnswerRelMean * 100).toFixed(1) }}%
                  </el-tag>
                </p>
                <p><strong>答案相关性 (OpenAI模型):</strong> 
                  <el-tag :type="getScoreType(singleAnswerRelOpenaiMean)">
                    {{ (singleAnswerRelOpenaiMean * 100).toFixed(1) }}%
                  </el-tag>
                </p>
              </div>
            </el-card>
          </div>
        </div>
      </div>
      
      <!-- 测试数据选择 -->
      <div class="card">
        <div class="card-header">
          <el-icon><Document /></el-icon> 测试数据集
        </div>
        <div class="card-body">
          <div class="button-group">
            <el-button @click="loadTestData" :loading="isLoading">
              {{ isLoading ? '加载中...' : '加载测试数据' }}
            </el-button>
            <el-button 
              type="success" 
              @click="evaluateSelected" 
              :disabled="!selectedItems.length || isLoading"
            >
              {{ isLoading ? '评估中...' : `评估选中项 (${selectedItems.length})` }}
            </el-button>
            <el-button 
              type="danger" 
              @click="clearSelection" 
              :disabled="!selectedItems.length"
            >
              清空选择
            </el-button>
          </div>
          
          <div v-if="testData.length > 0" class="test-data-list">
            <el-card 
              v-for="(item, index) in testData" 
              :key="index" 
              :class="['test-data-item', { selected: selectedItems.includes(index) }]"
              @click="toggleSelection(index)"
              shadow="hover"
            >
              <div class="test-data-question">{{ item.question }}</div>
              <div class="test-data-sql">{{ item.sql }}</div>
            </el-card>
          </div>
          
          <div v-if="testData.length === 0 && !isLoading" class="loading">
            <el-empty description="点击'加载测试数据'按钮加载数据集" />
          </div>
        </div>
      </div>
      
      <!-- 批量评估结果 -->
      <div v-if="batchResult" class="card">
        <div class="card-header">
          <el-icon><DataAnalysis /></el-icon> 批量评估结果
        </div>
        <div class="card-body">
          <div class="stats-grid">
            <el-card class="stat-card">
              <div class="stat-value">{{ batchResult.summary.total_evaluations }}</div>
              <div class="stat-label">总评估数</div>
            </el-card>
            <el-card class="stat-card">
              <div class="stat-value">{{ (batchResult.summary.exact_match_rate * 100).toFixed(1) }}%</div>
              <div class="stat-label">完全匹配率</div>
            </el-card>
            <el-card class="stat-card">
              <div class="stat-value">{{ (batchResult.summary.average_semantic_score * 100).toFixed(1) }}%</div>
              <div class="stat-label">平均语义相似度</div>
            </el-card>
          </div>
          
          <el-card v-if="batchResult.summary.ragas_evaluation || batchResult.summary.ragasEvaluation" class="result-card">
            <template #header>
              <div class="result-title">RAGAS整体评估</div>
              <div class="models-line" v-if="batchEmbeddingsName || batchLlmName">
                模型：LLM {{ batchLlmName || 'N/A' }} ｜ Embeddings {{ batchEmbeddingsName || 'N/A' }}
              </div>
            </template>
            <div class="result-content">
              <p><strong>忠实度:</strong> 
                <el-tag :type="getScoreType(batchFaithfulnessMean)">
                  {{ (batchFaithfulnessMean * 100).toFixed(1) }}%
                </el-tag>
              </p>
              <p><strong>答案相关性 (开源模型):</strong> 
                <el-tag :type="getScoreType(batchAnswerRelMean)">
                  {{ (batchAnswerRelMean * 100).toFixed(1) }}%
                </el-tag>
              </p>
              <p><strong>答案相关性 (OpenAI模型):</strong> 
                <el-tag :type="getScoreType(batchAnswerRelOpenaiMean)">
                  {{ (batchAnswerRelOpenaiMean * 100).toFixed(1) }}%
                </el-tag>
              </p>
            </div>
          </el-card>
          
          <el-card class="result-card">
            <template #header>
              <div class="result-title">详细结果</div>
            </template>
            <div class="result-content">
              <el-collapse>
                <el-collapse-item 
                  v-for="(result, index) in batchResult.results" 
                  :key="index"
                  :title="`问题 ${index + 1}: ${result.question}`"
                >
                  <p><strong>问题:</strong> {{ result.question }}</p>
                  <p><strong>预测SQL:</strong> <code>{{ result.predicted_sql }}</code></p>
                  <p><strong>标准SQL:</strong> <code>{{ result.ground_truth_sql }}</code></p>
                  <p><strong>完全匹配:</strong> 
                    <el-tag :type="result.sql_accuracy.exact_match ? 'success' : 'danger'">
                      {{ result.sql_accuracy.exact_match ? '是' : '否' }}
                    </el-tag>
                  </p>
                  <p><strong>语义相似度:</strong> 
                    <el-tag :type="getScoreType(result.sql_accuracy.semantic_score)">
                      {{ (result.sql_accuracy.semantic_score * 100).toFixed(1) }}%
                    </el-tag>
                  </p>
                </el-collapse-item>
              </el-collapse>
            </div>
          </el-card>
        </div>
      </div>
      
      <!-- 消息提示 -->
      <el-alert
        v-if="errorMessage"
        :title="errorMessage"
        type="error"
        :closable="false"
        show-icon
        class="message-alert"
      />
      
      <el-alert
        v-if="successMessage"
        :title="successMessage"
        type="success"
        :closable="false"
        show-icon
        class="message-alert"
      />
    </div>
  </div>
</template>

<script>
import { ref, onMounted, computed } from 'vue';
import { ElMessage } from 'element-plus';
import axios from 'axios';

export default {
  name: 'App',
  setup() {
    // 响应式数据
    const singleEvaluation = ref({
      question: '',
      predictedSql: '',
      groundTruthSql: ''
    });
    const singleResult = ref(null);
    const testData = ref([]);
    const selectedItems = ref([]);
    const batchResult = ref(null);
    const isLoading = ref(false);
    const errorMessage = ref('');
    const successMessage = ref('');
    const apiBaseUrl = 'http://localhost:5000/api';

    // 安全取值
    const safeGet = (obj, path) => {
      try {
        return path.split('.').reduce((o, k) => (o && o[k] !== undefined && o[k] !== null) ? o[k] : undefined, obj);
      } catch {
        return undefined;
      }
    };

    // 单条 - 计算Ragas显示值
    const singleFaithfulnessMean = computed(() => {
      const r = singleResult.value;
      const v = safeGet(r, 'ragasEvaluation.faithfulness.mean') ?? safeGet(r, 'ragas_evaluation.faithfulness.mean');
      return typeof v === 'number' ? v : 0;
    });
    const singleAnswerRelMean = computed(() => {
      const r = singleResult.value;
      const v = safeGet(r, 'ragasEvaluation.answerRelevancy.mean')
        ?? safeGet(r, 'ragas_evaluation.answer_relevancy_opensource.mean')
        ?? safeGet(r, 'ragas_evaluation.answer_relevancy.mean');
      return typeof v === 'number' ? v : 0;
    });
    const singleAnswerRelOpenaiMean = computed(() => {
      const r = singleResult.value;
      const v = safeGet(r, 'ragasEvaluation.answerRelevancyOpenai.mean')
        ?? safeGet(r, 'ragas_evaluation.answer_relevancy_openai.mean');
      return typeof v === 'number' ? v : 0;
    });
    const singleEmbeddingsName = computed(() => {
      const r = singleResult.value;
      return safeGet(r, 'models.embeddings') || '';
    });
    const singleLlmName = computed(() => {
      const r = singleResult.value;
      return safeGet(r, 'models.llm') || '';
    });

    // 批量 - 计算Ragas显示值
    const batchFaithfulnessMean = computed(() => {
      const r = batchResult.value?.summary;
      const v = safeGet(r, 'ragasEvaluation.faithfulness.mean') ?? safeGet(r, 'ragas_evaluation.faithfulness.mean');
      return typeof v === 'number' ? v : 0;
    });
    const batchAnswerRelMean = computed(() => {
      const r = batchResult.value?.summary;
      const v = safeGet(r, 'ragasEvaluation.answerRelevancy.mean')
        ?? safeGet(r, 'ragas_evaluation.answer_relevancy_opensource.mean')
        ?? safeGet(r, 'ragas_evaluation.answer_relevancy.mean');
      return typeof v === 'number' ? v : 0;
    });
    const batchAnswerRelOpenaiMean = computed(() => {
      const r = batchResult.value?.summary;
      const v = safeGet(r, 'ragasEvaluation.answerRelevancyOpenai.mean')
        ?? safeGet(r, 'ragas_evaluation.answer_relevancy_openai.mean');
      return typeof v === 'number' ? v : 0;
    });
    const batchEmbeddingsName = computed(() => {
      const r = batchResult.value?.summary;
      return safeGet(r, 'models.embeddings') || '';
    });
    const batchLlmName = computed(() => {
      const r = batchResult.value?.summary;
      return safeGet(r, 'models.llm') || '';
    });

    // 方法
    const evaluateSingle = async () => {
      if (!singleEvaluation.value.predictedSql || !singleEvaluation.value.groundTruthSql) {
        ElMessage.error('请填写预测SQL和标准SQL');
        return;
      }
      
      isLoading.value = true;
      clearMessages();
      
      try {
        const response = await axios.post(`${apiBaseUrl}/evaluate-sql`, {
          question: singleEvaluation.value.question,
          predicted_sql: singleEvaluation.value.predictedSql,
          ground_truth_sql: singleEvaluation.value.groundTruthSql
        });
        
        if (response.data.success) {
          singleResult.value = response.data;
          ElMessage.success('单个SQL评估完成');
        } else {
          ElMessage.error(response.data.error || '评估失败');
        }
      } catch (error) {
        ElMessage.error('评估请求失败: ' + error.message);
      } finally {
        isLoading.value = false;
      }
    };

    const loadTestData = async () => {
      isLoading.value = true;
      clearMessages();
      
      try {
        const response = await axios.get(`${apiBaseUrl}/test-data`);
        
        if (response.data.success) {
          testData.value = response.data.data;
          ElMessage.success(`成功加载 ${response.data.count} 条测试数据`);
        } else {
          ElMessage.error(response.data.error || '加载测试数据失败');
        }
      } catch (error) {
        ElMessage.error('加载测试数据失败: ' + error.message);
      } finally {
        isLoading.value = false;
      }
    };

    const toggleSelection = (index) => {
      const idx = selectedItems.value.indexOf(index);
      if (idx > -1) {
        selectedItems.value.splice(idx, 1);
      } else {
        selectedItems.value.push(index);
      }
    };

    const clearSelection = () => {
      selectedItems.value = [];
    };

    const evaluateSelected = async () => {
      if (selectedItems.value.length === 0) {
        ElMessage.error('请选择要评估的项目');
        return;
      }
      
      isLoading.value = true;
      clearMessages();
      
      try {
        const evaluations = selectedItems.value.map(index => ({
          question: testData.value[index].question,
          predicted_sql: testData.value[index].sql,
          ground_truth_sql: testData.value[index].sql
        }));
        
        const response = await axios.post(`${apiBaseUrl}/evaluate-batch`, {
          evaluations: evaluations
        });
        
        if (response.data.success) {
          batchResult.value = response.data;
          ElMessage.success(`批量评估完成，共评估 ${response.data.summary.total_evaluations} 项`);
        } else {
          ElMessage.error(response.data.error || '批量评估失败');
        }
      } catch (error) {
        ElMessage.error('批量评估失败: ' + error.message);
      } finally {
        isLoading.value = false;
      }
    };

    const getScoreType = (score) => {
      if (score >= 0.8) return 'success';
      if (score >= 0.6) return 'warning';
      return 'danger';
    };

    const showError = (message) => {
      errorMessage.value = message;
      successMessage.value = '';
      setTimeout(() => {
        errorMessage.value = '';
      }, 5000);
    };

    const showSuccess = (message) => {
      successMessage.value = message;
      errorMessage.value = '';
      setTimeout(() => {
        successMessage.value = '';
      }, 3000);
    };

    const clearMessages = () => {
      errorMessage.value = '';
      successMessage.value = '';
    };

    onMounted(() => {
      loadTestData();
    });

    return {
      singleEvaluation,
      singleResult,
      testData,
      selectedItems,
      batchResult,
      isLoading,
      errorMessage,
      successMessage,
      evaluateSingle,
      loadTestData,
      toggleSelection,
      clearSelection,
      evaluateSelected,
      getScoreType,
      showError,
      showSuccess,
      clearMessages,
      singleFaithfulnessMean,
      singleAnswerRelMean,
      singleAnswerRelOpenaiMean,
      batchFaithfulnessMean,
      batchAnswerRelMean,
      batchAnswerRelOpenaiMean,
      singleEmbeddingsName,
      singleLlmName,
      batchEmbeddingsName,
      batchLlmName
    };
  }
};
</script>

<style scoped>
/* 样式将在 main.css 中定义 */
.models-line {
  margin-top: 6px;
  color: #666;
  font-size: 12px;
}
</style>
